Bloquear Navegacion (canDeactivate)

Descripcion

Como bloquear el routing haciendo que salga un pop-up para verificar que queremos navegar a un nuevo componente y a mayores explicar como bloquear el refresco de la página (F5) o el cierre del navegador, preguntando antes de salir, en una posible situación de cambios sin guardar.

Codigo de Ejemplo
Metodo

Tenemos una aplicación con un componente principal y despues dos componentes entre los que navegaremos, Inicio y Contenido

En el componente Contenido será donde bloquearemos la navegación y por lo tanto nos saldrá un pop-up para confirmar si queremos navegar a otro componente

Primero creamos un archivo bloqueo-routing.ts en la raiz del proyecto (a la altura del archivo del routing) con el siguiente contenido:

import { CanDeactivate } from '@angular/router'
import { ContenidoComponent } from './contenido/contenido.component'

export class BloqueoRouting implements CanDeactivate<ContenidoComponent> {
canDeactivate(component: ContenidoComponent): boolean {
  return confirm('Quieres dejar la página?');
}
}

Tanto en el generic de la implementación de la clase como en el parametro de la funcion tenemos que indicar el componente del que queremos tener acceso para leer ciertos parámetros, en este caso ContenidoComponent, no lo usaremos ahora, pero si en un ejemplo posterior en el que haremos el bloqueo condicional.

A continuación en el archivo app-routing.module.ts declaramos el provider BloqueoRouting y en las rutas indicamos cual será la ruta que tenga el bloqueo usando:

canDeactivate: [BloqueoRouting]

Una vez hecho esto el bloqueo ya está listo, si navegamos a la página de contenido e intentamos volver a inicio veremos el siguiente mensaje:

Si pulsamos Ok se realizará la navegación, si pulsamos Cancel se nos mantendrá en la página actual


Bloqueo Condicional

El bloqueo podemos establecerlo de manera que se cumpla una condicion y no esté siempre activo (que no se nos muestre siempre el pop-up preguntando si queremos continuar o no)

En el componente Contenido declaramos un variable bloqueo para determinar si tenemos que bloquear la navegación o no, está variable la controlaremos con un boton que la pondrá a true.

En el archivo bloqueo-routing.ts utilizamos la variable bloqueo para decidir si se bloquea la navegación o no, la funcion canDeactivate previene la navegación si se devuelve false y la permite si se devuelve true, en el caso del confirm si se pulsa OK en el pop-up devuelve true si se pulsa CANCEL devuelve false:

import { CanDeactivate } from '@angular/router'
import { ContenidoComponent } from './contenido/contenido.component'

export class BloqueoRouting implements CanDeactivate<ContenidoComponent> {
  canDeactivate(component: ContenidoComponent): boolean {
    if(component.bloqueo){
      return confirm('Quieres dejar la página?');
    }
    return true;
  }
}

Con esta implementación podemos navegar entre el componente Inicio y el componente Contenido y solo se bloqueará la navegación y se mostrará el pop-up cuando pulsemos el boton en el componente Contenido, de esta manera podemos controlar que se bloquee la navegación cuando tengamos una posible situación de cambios no guardados.

Bloqueo al actualizar o cerrar el navegador

Con la implementación actual todavía nos queda un problema, cuando pulsamos F5 en el navegador o cuando cerramos el navegador, no se nos muestra ningún pop-up de confirmación y se cierra la página sin mas, pudiendo llevar a la perdida de información que no se haya guardado.

Para solucionar esto añadimos un @HostListener para el evento window:beforeunload en el componente Contenido tal que así:

@HostListener("window:beforeunload")
BloqueoActualizar(){
  return !this.bloqueo;
}

En la funcion asociada al @HostListener si devolvemos true se continuará con la navegación, si devolvemos false se mostrará el pop-up preguntando si queremos continuar o no, por lo tanto devolvemos la negación de la variable bloqueo.

Ahora tanto al intentar navegar dentro de la página como si intentamos actualizar o cerrar el navegador se nos preguntará si queremos continuar, evitando así un despiste del usuario en una situación en la que haya cambios pendientes de ser guardados.

NOTA: Cuando se intenta actualizar la página (F5) o cerrar el navegador, el mensaje que se muestra, es un mensaje estandar del propio navegador y no se puede personalizar.

Tags

canDeactivate | Routing | Angular